package com.codepoetics.protonpack; import java.util.Spliterator; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Stream; class InterleavingSpliterator<T> implements Spliterator<T> { public static <T> Spliterator<T> interleaving(Spliterator<T>[] spliterators, Function<T[], Integer> selector) { Supplier<T[]> bufferedValues = () -> { T[] values = (T[]) new Object[spliterators.length]; for (int i=0; i<spliterators.length; i++) { final int stableIndex = i; spliterators[i].tryAdvance(t -> values[stableIndex] = t); } return values; }; return new InterleavingSpliterator<>(spliterators, bufferedValues, selector); } private final Spliterator<T>[] spliterators; private final Supplier<T[]> bufferSupplier; private T[] buffer = null; private final Function<T[], Integer> selector; private InterleavingSpliterator(Spliterator<T>[] spliterators, Supplier<T[]> bufferSupplier, Function<T[], Integer> selector) { this.spliterators = spliterators; this.bufferSupplier = bufferSupplier; this.selector = selector; } @Override public boolean tryAdvance(Consumer<? super T> action) { if (buffer == null) { buffer = bufferSupplier.get(); } if (Stream.of(buffer).allMatch(Predicate.isEqual(null))) { return false; } int selected = selector.apply(buffer); action.accept(buffer[selected]); if (!spliterators[selected].tryAdvance(t -> buffer[selected] = t)) { buffer[selected] = null; } return true; } @Override public Spliterator<T> trySplit() { return null; } @Override public long estimateSize() { if (Stream.of(spliterators).anyMatch(s -> s.estimateSize() == Long.MAX_VALUE)) { return Long.MAX_VALUE; } return Stream.of(spliterators).mapToLong(Spliterator::estimateSize).sum(); } @Override public long getExactSizeIfKnown() { if (Stream.of(spliterators).allMatch(s -> s.hasCharacteristics(Spliterator.SIZED))) { return Stream.of(spliterators).mapToLong(Spliterator::getExactSizeIfKnown).sum(); } return -1; } @Override public int characteristics() { return Spliterator.NONNULL & Spliterator.ORDERED & Spliterator.IMMUTABLE; } }